'''
Авторские права принадлежат Алифатову Дмитрию Сергеевичу <dmitry@alifatov.ru>

Вы можете использовать данное Программное обеспечение на любом количестве компьютеров.

Не разрешается распространять Программное обеспечение, извлекать его компоненты и создавать производные работы на его основе.
'''
import os
import subprocess
import socket
import fcntl
import struct
import ctypes
import math
import array
import re
import ipaddress
import astra
from astra import system_call

SYSFS_NET_PATH = b"/sys/class/net"
PROCFS_NET_PATH = b"/proc/net/dev"

# definitions from the kernel header files.
# /* Routing table calls.  */
SIOCADDRT = 0x890B		# /* add routing table entry	*/
SIOCDELRT = 0x890C		# /* delete routing table entry	*/
SIOCRTMSG = 0x890D		# /* call to routing system	*/
# /* Socket configuration controls. */
SIOCGIFNAME = 0x8910		# /* get iface name		*/
SIOCSIFLINK = 0x8911		# /* set iface channel		*/
SIOCGIFCONF = 0x8912		# /* get iface list		*/
SIOCGIFFLAGS = 0x8913		# /* get flags			*/
SIOCSIFFLAGS = 0x8914		# /* set flags			*/
SIOCGIFADDR = 0x8915		# /* get PA address		*/
SIOCSIFADDR = 0x8916		# /* set PA address		*/
SIOCGIFDSTADDR = 0x8917		# /* get remote PA address	*/
SIOCSIFDSTADDR = 0x8918		# /* set remote PA address	*/
SIOCGIFBRDADDR = 0x8919		# /* get broadcast PA address	*/
SIOCSIFBRDADDR = 0x891a		# /* set broadcast PA address	*/
SIOCGIFNETMASK = 0x891b		# /* get network PA mask		*/
SIOCSIFNETMASK = 0x891c		# /* set network PA mask		*/
SIOCGIFMETRIC = 0x891d		# /* get metric			*/
SIOCSIFMETRIC = 0x891e		# /* set metric			*/
SIOCGIFMEM = 0x891f		# /* get memory address (BSD)	*/
SIOCSIFMEM = 0x8920		# /* set memory address (BSD)	*/
SIOCGIFMTU = 0x8921		# /* get MTU size			*/
SIOCSIFMTU = 0x8922		# /* set MTU size			*/
SIOCSIFNAME = 0x8923		# /* set interface name		*/
SIOCSIFHWADDR = 0x8924		# /* set hardware address 	*/
SIOCGIFENCAP = 0x8925		# /* get/set encapsulations       */
SIOCSIFENCAP = 0x8926
SIOCGIFHWADDR = 0x8927		# /* Get hardware address		*/
SIOCGIFSLAVE = 0x8929		# /* Driver slaving support	*/
SIOCSIFSLAVE = 0x8930
SIOCADDMULTI = 0x8931		# /* Multicast address lists	*/
SIOCDELMULTI = 0x8932
SIOCGIFINDEX = 0x8933		# /* name -> if_index mapping	*/
SIOGIFINDEX = SIOCGIFINDEX  # /* misprint compatibility :-)	*/
SIOCSIFPFLAGS = 0x8934		# /* set/get extended flags set	*/
SIOCGIFPFLAGS = 0x8935
SIOCDIFADDR = 0x8936		# /* delete PA address		*/
SIOCSIFHWBROADCAST = 0x8937 # /* set hardware broadcast addr	*/
SIOCGIFCOUNT = 0x8938		# /* get number of devices */
SIOCGIFBR = 0x8940		# /* Bridging support		*/
SIOCSIFBR = 0x8941		# /* Set bridging options 	*/
SIOCGIFTXQLEN = 0x8942		# /* Get the tx queue length	*/
SIOCSIFTXQLEN = 0x8943		# /* Set the tx queue length 	*/
# /* ARP cache control calls. */
SIOCDARP = 0x8953		# /* delete ARP table entry	*/
SIOCGARP = 0x8954		# /* get ARP table entry		*/
SIOCSARP = 0x8955		# /* set ARP table entry		*/
# /* RARP cache control calls. */
SIOCDRARP = 0x8960		# /* delete RARP table entry	*/
SIOCGRARP = 0x8961		# /* get RARP table entry		*/
SIOCSRARP = 0x8962		# /* set RARP table entry		*/
# /* Driver configuration calls */
SIOCGIFMAP = 0x8970		# /* Get device parameters	*/
SIOCSIFMAP = 0x8971		# /* Set device parameters	*/
# /* DLCI configuration calls */
SIOCADDDLCI = 0x8980		# /* Create new DLCI device	*/
SIOCDELDLCI = 0x8981		# /* Delete DLCI device		*/

SIOCETHTOOL = 0x8946

# From linux/if.h
IFF_UP = 0x1

# From linux/socket.h
AF_UNIX = 1
AF_INET = 2

# From linux/ethtool.h
ETHTOOL_GSET = 0x00000001   # Get settings
ETHTOOL_SSET = 0x00000002   # Set settings
ETHTOOL_GLINK = 0x0000000a      # Get link status (ethtool_value)
ETHTOOL_SPAUSEPARAM = 0x00000013    # Set pause parameters.

ADVERTISED_10baseT_Half = (1 << 0)
ADVERTISED_10baseT_Full =(1 << 1)
ADVERTISED_100baseT_Half = (1 << 2)
ADVERTISED_100baseT_Full = (1 << 3)
ADVERTISED_1000baseT_Half = (1 << 4)
ADVERTISED_1000baseT_Full = (1 << 5)
ADVERTISED_Autoneg = (1 << 6)
ADVERTISED_TP = (1 << 7)
ADVERTISED_AUI = (1 << 8)
ADVERTISED_MII = (1 << 9)
ADVERTISED_FIBRE = (1 << 10)
ADVERTISED_BNC = (1 << 11)
ADVERTISED_10000baseT_Full = (1 << 12)

# This is probably not cross-platform
SIZE_OF_IFREQ = 40

# Globals
sock = None
sockfd = None

def verify_ip_subnet(ip_address, subnet_address):
    for address in ipaddress.ip_network(subnet_address):
        if str(address) == str(ip_address):
            return True
    return False


class Interface():
    ''' Class representing a Linux network device. '''
    def __init__(self, name):
        self.name = name
        self.type = ""

        self.master = ""
        self.vlans_count = 0
        self.mac_change = False
        self.bridge_member = False
        self.bond_member = False
        self.dns1 = ""
        self.dns2 = ""
        path = '/sys/class/net/' + name
        if os.path.isdir(path):
            self.index = self.get_index()
            self.hwaddress = self.get_mac()
            self.mtu = self.get_mtu()
            if self.is_dhcp():
                self.inet = "dhcp"
            elif self.get_ip():
                self.inet = "static"
            else:
                self.inet = "manual"
            if self.inet != "manual":
                self.ip = self.get_ip()
                self.netmask = self.get_netmask(cidr=True)
                self.gateway = self.get_gateway()
                dns = []
                with open("/etc/resolv.conf") as resolv_file:
                    for line in resolv_file:
                        if "nameserver" in line:
                            trimmed_line = "".join([i for i in line if i not in [" ", "\t", "\n"]])
                            dns.append(trimmed_line[trimmed_line.index("nameserver") + len("nameserver"):])
                for i in range(len(dns), 2):
                    dns.append("")
                if self.gateway != "":
                    self.dns1 = dns[0]
                    self.dns2 = dns[1]
            else:
                self.ip = ""
                self.netmask = ""
                self.gateway = ""
            self.link_up = self.is_up()
            if self.link_up:
                self.link_speed = self.get_link_speed()
                self.link_auto = self.is_link_auto()
                self.link_duplexx = self.is_link_duplex()
            else:
                self.link_speed = ""
                self.link_auto = ""
                self.link_duplexx = ""
        else:
            self.index = ""
            self.hwaddress = ""
            self.mtu = "1500"
            self.inet = "manual"
            self.ip = ""
            self.netmask = ""
            self.gateway = ""
            self.link_speed = ""
            self.link_auto = ""
            self.link_duplexx = ""

    def up(self):
        ''' Bring up the bridge interface. Equivalent to ifconfig [iface] up. '''
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')

        # Get existing device flags
        ifreq = struct.pack('16sh', ifname, 0)
        try:
            ifs = fcntl.ioctl(sockfd, SIOCGIFFLAGS, ifreq)
            flags = struct.unpack('16sh', ifs)[1]
        except:
            return -1
        # Set new flags
        flags = flags | IFF_UP
        ifreq = struct.pack('16sh', ifname, flags)
        try:
            fcntl.ioctl(sockfd, SIOCSIFFLAGS, ifreq)
        except:
            return -1

    def down(self):
        ''' Bring down the bridge interface. Equivalent to ifconfig [iface] down. '''
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')

        # Get existing device flags
        ifreq = struct.pack('16sh', ifname, 0)
        try:
            ifs = fcntl.ioctl(sockfd, SIOCGIFFLAGS, ifreq)
            flags = struct.unpack('16sh', ifs)[1]
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return False
        # Set new flags
        flags = flags & ~IFF_UP
        ifreq = struct.pack('16sh', ifname, flags)
        try:
            fcntl.ioctl(sockfd, SIOCSIFFLAGS, ifreq)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return False

    def is_up(self):
        ''' Return True if the interface is up, False otherwise. '''
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')

        # Get existing device flags
        ifreq = struct.pack('16sh', ifname, 0)
        try:
            ifs = fcntl.ioctl(sockfd, SIOCGIFFLAGS, ifreq)
            flags = struct.unpack('16sh', ifs)[1]
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return False

        # Return flags
        if flags & IFF_UP:
            return True
        else:
            return False

    def is_exist(self):
        exist = False
        interfaces = socket.if_nameindex()
        # enumerate all exist interfaces
        for interface in interfaces:
            name = str(interface[1])
            if self.name == name:
                exist = True
                continue
        return exist

    def get_mtu(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')
        ifr = struct.pack('<16sH', ifname, 0)
        try:
            ifs = fcntl.ioctl(s, 0x8921, ifr)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return False
        mtu = struct.unpack('<H', ifs[16:18])[0]
        return mtu

    def get_mac(self):
        ''' Obtain the device's mac address. '''
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')

        ifreq = struct.pack('16sH14s', ifname, AF_UNIX, b'\x00' * 14)
        try:
            ifs = fcntl.ioctl(sockfd, SIOCGIFHWADDR, ifreq)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return False

        address = struct.unpack('16sH14s', ifs)[2]
        mac = struct.unpack('6B8x', address)

        return ":".join(['%02X' % i for i in mac])

    def get_permaddr(self):
        permaddr = system_call(f"ip -0 a show {self.name} | grep permaddr")
        if permaddr != "":
            return permaddr[-18:]
        else:
            return self.get_mac()

    def set_mac(self, newmac):
        ''' Set the device's mac address. Device must be down for this to
            succeed. '''
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')

        macbytes = [int(i, 16) for i in newmac.split(':')]
        ifr = struct.pack('16sH6B8x', ifname, AF_UNIX, *macbytes)
        try:
            fcntl.ioctl(sockfd, SIOCSIFHWADDR, ifr)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return False

    def get_ip(self):
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')

        ifr = struct.pack('16sH14s', ifname, AF_INET, b'\x00' * 14)
        try:
            ifs = fcntl.ioctl(sockfd, SIOCGIFADDR, ifr)
        except IOError:
            return None
        ip = struct.unpack('16sH2x4s8x', ifs)[2]
        return socket.inet_ntoa(ip)

    def set_ip(self, newip):
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')
        ipbytes = socket.inet_aton(newip)
        ifreq = struct.pack('16sH2s4s8s', ifname, AF_INET, b'\x00' * 2, ipbytes, b'\x00' * 8)
        try:
            fcntl.ioctl(sockfd, SIOCSIFADDR, ifreq)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return False

    def get_netmask(self, cidr = False):
        # if cidr = True then use /24 format
        # if cidr = False then use 255.255.255.0 format
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')
        if cidr:
            ifreq = struct.pack('16sH14s', ifname, AF_INET, b'\x00' * 14)
            try:
                res = fcntl.ioctl(sockfd, SIOCGIFNETMASK, ifreq)
            except IOError:
                return 0
            netmask = socket.ntohl(struct.unpack('16sH2xI8x', res)[2])
            mask = 32 - int(round(math.log(ctypes.c_uint32(~netmask).value + 1, 2), 1))
        else:
            ifreq = struct.pack('256s', ifname)
            try:
                ifs = fcntl.ioctl(sockfd, 0x891b, ifreq)
                mask = socket.inet_ntoa(ifs[20:24])
            except Exception as err:
                print(f"Unexpected {err}, {type(err)}")
                return None
        return mask

    def set_netmask(self, netmask):
        #netmask in CIDR format
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')
        netmask = ctypes.c_uint32(~((2 ** (32 - netmask)) - 1)).value
        nmbytes = socket.htonl(netmask)
        ifreq = struct.pack('16sH2sI8s', ifname, AF_INET, b'\x00' * 2, nmbytes, b'\x00' * 8)
        try:
            fcntl.ioctl(sockfd, SIOCSIFNETMASK, ifreq)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return None

    def get_gateway(self):
        ifname = str(self.name)
        gateway = None
        with open("/proc/net/route") as fh:
            # skip header
            next(fh)
            for line in fh:
                routes = line.strip().split()
                destination = socket.inet_ntoa(struct.pack("<L", int(routes[1], 16)))
                interface = routes[0]
                if destination != "0.0.0.0":
                    continue
                if interface != ifname:
                    continue
                gateway = socket.inet_ntoa(struct.pack("<L", int(routes[2], 16)))
        return gateway

    def is_dhcp(self):
        if self.get_ip():
            command = "ip a | grep " + self.get_ip()
        else:
            return False
        p = subprocess.check_output(command, shell=True, text=True)
        if "dynamic" in p:
            return True
        else:
            return False

    def get_index(self):
        ''' Convert an interface name to an index value. '''
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')
        ifreq = struct.pack('16si', ifname, 0)
        try:
            res = fcntl.ioctl(sockfd, SIOCGIFINDEX, ifreq)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return None
        return struct.unpack("16si", res)[1]

    def get_link_info(self):
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')

        # First get link params
        ecmd = array.array('B', struct.pack('I39s', ETHTOOL_GSET, b'\x00' * 39))
        ifreq = struct.pack('16sP', ifname, ecmd.buffer_info()[0])
        try:
            fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
            res = ecmd.tostring()
            speed, duplex, auto = struct.unpack('12xHB3xB24x', res)
        except IOError:
            speed, duplex, auto = 65535, 255, 255

        # Then get link up/down state
        ecmd = array.array('B', struct.pack('2I', ETHTOOL_GLINK, 0))
        ifreq = struct.pack('16sP', ifname, ecmd.buffer_info()[0])
        try:
            fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return None
        res = ecmd.tostring()
        up = bool(struct.unpack('4xI', res)[0])

        if speed == 65535:
            speed = 0
        if duplex == 255:
            duplex = None
        else:
            duplex = bool(duplex)
        if auto == 255:
            auto = None
        else:
            auto = bool(auto)
        return speed, duplex, auto, up

    def get_link_speed(self):
        #speed in mbps
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')

        # First get link params
        ecmd = array.array('B', struct.pack('I39s', ETHTOOL_GSET, b'\x00' * 39))
        ifreq = struct.pack('16sP', ifname, ecmd.buffer_info()[0])
        try:
            fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
            res = ecmd.tobytes()
            speed, duplex, auto = struct.unpack('12xHB3xB24x', res)
        except IOError:
            speed = 65535
        if speed == 65535:
            speed = 0
        return speed

    def is_link_duplex(self):
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')

        # First get link params
        ecmd = array.array('B', struct.pack('I39s', ETHTOOL_GSET, b'\x00' * 39))
        ifreq = struct.pack('16sP', ifname, ecmd.buffer_info()[0])
        try:
            fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
            res = ecmd.tostring()
            speed, duplex, auto = struct.unpack('12xHB3xB24x', res)
        except IOError:
            duplex = None
        duplex = bool(duplex)
        return duplex

    def is_link_auto(self):
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')

        # First get link params
        ecmd = array.array('B', struct.pack('I39s', ETHTOOL_GSET, b'\x00' * 39))
        ifreq = struct.pack('16sP', ifname, ecmd.buffer_info()[0])
        try:
            fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
            res = ecmd.tostring()
            speed, duplex, auto = struct.unpack('12xHB3xB24x', res)
        except IOError:
            auto = None
        auto = bool(auto)
        return auto

    def set_link_mode(self, speed, duplex):
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')
        # First get the existing info
        ecmd = array.array('B', struct.pack('I39s', ETHTOOL_GSET, b'\x00' * 39))
        ifreq = struct.pack('16sP', ifname, ecmd.buffer_info()[0])
        try:
            fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return None
        # Then modify it to reflect our needs
        ecmd[0:4] = array.array('B', struct.pack('I', ETHTOOL_SSET))
        ecmd[12:14] = array.array('B', struct.pack('H', speed))
        ecmd[14] = int(duplex)
        ecmd[18] = 0  # Autonegotiation is off
        try:
            fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return None

    def set_link_auto(self, ten=True, hundred=True, thousand=True):
        sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        ifname = bytes(str(self.name), 'utf-8')
        # First get the existing info
        ecmd = array.array('B', struct.pack('I39s', ETHTOOL_GSET, b'\x00' * 39))
        ifreq = struct.pack('16sP', ifname, ecmd.buffer_info()[0])
        try:
            fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return None

        # Then modify it to reflect our needs
        ecmd[0:4] = array.array('B', struct.pack('I', ETHTOOL_SSET))

        advertise = 0
        if ten:
            advertise |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full
        if hundred:
            advertise |= ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full
        if thousand:
            advertise |= ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full

        newmode = struct.unpack('I', ecmd[4:8].tostring())[0] & advertise
        ecmd[8:12] = array.array('B', struct.pack('I', newmode))
        ecmd[18] = 1
        try:
            fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
        except Exception as err:
            print(f"Unexpected {err}, {type(err)}")
            return None

    """    
    def set_pause_param(self, autoneg, rx_pause, tx_pause):
        #
        # Ethernet has flow control! The inter-frame pause can be adjusted, by
        # auto-negotiation through an ethernet frame type with a simple two-field
        # payload, and by setting it explicitly.
        # http://en.wikipedia.org/wiki/Ethernet_flow_control
        
        # create a struct ethtool_pauseparm
        # create a struct ifreq with its .ifr_data pointing at the above
        ecmd = array.array('B', struct.pack('IIII',
                                            ETHTOOL_SPAUSEPARAM, bool(autoneg), bool(rx_pause), bool(tx_pause)))
        buf_addr, _buf_len = ecmd.buffer_info()
        ifreq = struct.pack('16sP', self.name, buf_addr)
        fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)

    """

    def get_stats(self):
        spl_re = re.compile(br"\s+")

        fp = open(PROCFS_NET_PATH, 'rb')
        # Skip headers
        fp.readline()
        fp.readline()
        stats = ""
        while True:
            data = fp.readline()
            if not data:
                return None

            name, stats_str = data.split(b":")
            if name.strip() != self.name:
                continue

            stats = [int(a) for a in spl_re.split(stats_str.strip())]
            break

        titles = ["rx_bytes", "rx_packets", "rx_errs", "rx_drop", "rx_fifo",
                  "rx_frame", "rx_compressed", "rx_multicast", "tx_bytes",
                  "tx_packets", "tx_errs", "tx_drop", "tx_fifo", "tx_colls",
                  "tx_carrier", "tx_compressed"]
        return dict(list(zip(titles, stats)))

class Ethernet_interface(Interface):
    def __init__(self, name):
        super().__init__(name)
        self.type = "ethernet"

        path = '/sys/class/net/' + name

        #проверяемя является интерфейс членом объединения или моста

        if os.path.isdir(path + '/bonding_slave'):
            # bond_members
            self.bond_member = True
            self.master = os.path.realpath(path + '/master').split('/')[-1]
        elif os.path.isdir(path + '/brport'):
            # bridge member
            self.bridge_member = True
            self.master = os.path.realpath(path + '/master').split('/')[-1]


    def get_init_script(self, all=False):
        script = "\n"
        if self.up and not self.bridge_member:
            script = script + str(f"auto {self.name}\n")
        if self.inet == "dhcp":
            script = script + str(f"iface {self.name} inet dhcp\n")
        elif self.inet == "static":
            script = script + str(f"iface {self.name} inet static\n")
            script = script + str(f"\taddress {self.ip}/{self.netmask}\n")
            if self.gateway:
                script = script + str(f"\tgateway {self.gateway}\n")
            if self.dns1 != "":
                self.fqdn = os.uname().nodename
                self.hostname = self.fqdn.split(".")[0]
                self.domainname = self.fqdn[self.fqdn.index(self.hostname) + len(self.hostname) + 1:]
                if self.domainname != "":
                    script = script + str(f"\tdns-search {self.domainname}\n")
                dns_nameservers = str(self.dns1)
                if self.dns2 != "":
                    dns_nameservers = dns_nameservers + f" {str(self.dns2)}"
                script = script + str(f"\tdns-nameservers {dns_nameservers}\n")
        else:
            script = script + str(f"iface {self.name} inet manual\n")
        if self.master != "" and self.bond_member:
            script = script + str(f"\tbond-master {self.master}\n")
        # mtu.
        if str(self.mtu) != "1500" or all:
            script = script + str(f"\tmtu {self.mtu}\n")
        return script

class Bond(Interface):
    # BOND-RELATED OPTIONS
    bond_members = [],  # bond-members list of interfaces. Denotes the physical member interfaces to form this LAG. For compatiblity to ifupdown1 and ifupdown2 slaves as well as bond-slaves are an alias for this option. This option is required.
    bond_mode = "balance-rr",  # bond-mode mode. Denotes the mode for this LAG. The mode can be given as string or as numerical value. Valid values are balance-rr (0), active-backup (1), balance-xor (2), broadcast (3), 802.3ad (4), balance-tlb (5), balance-alb (6). The default is balance-rr
    bond_xmit_hash_policy = "layer2",  # bond-xmit-hash-policy policy. Denotes the hash policy/algorithm used to distribute packets across the physical links. This only applies for modes balance-alb, balance-tlb, balance-xor, and 802.3ad. The policy can be given as string or as numerical value. Valid values are layer2 (0), layer3+4 (1), layer2+3 (2), encap2+3 (3), and encap3+4 (4). The default is layer2.
    bond_min_links = "0",  # Denotes the minimum number of available links before turning on carrier.
    bond_miimon = "0",  # Denotes the MII link monitoring frequency in milliseconds. This determines how often the link state of each slave is inspected for link failures. A value of zero disables MII link monitoring. The default is 0.
    bond_use_carrier = "1",  # Denotes wether miimon uses MII or ethtool ioctls vs. the netif_carrier_ok() call to determine member link status. A value of 1 enables the use of netif_carrier_ok(), a value of 0 will use the deprecated MII / ETHTOOL ioctls. The default is 1.
    bond_updelay = "0",  # Denotes the delay in milliseconds before considering link up, in milliseconds. The default is 0.
    bond_downdelay = "0",  # Denotes the delay in milliseconds before considering link down, in milliseconds. The default is 0.
    bond_all_slaves_active = "0",  # Denotes wether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1). The default is 0.
    bond_packets_per_slave = "1",  # Denotes the number of packets to transmit through a member before moving to the next one. When set to 0 then a slave is chosen at random. The valid range is 0 - 65535; the default value is 1. This option has effect only in balance-rr mode.
    bond_lp_interval = "1",  # Denotes the interval in seconds between sending learning packets to each members peer switch. The valid range is 1 - 0x7fffffff; the default value is 1. This option has effect only in modes balance-tlb and balance-alb.
    bond_resend_igmp = "1",  # Denotes the number of IGMP membership reports to send after a link failover happend. The valid range is 0 - 255; a value of 0 prevents the IGMP membership report from being issued in response to the failover event. The default is 1. This option is useful for bonding modes balance-rr, active-backup balance-tlb and balance-alb, in which a failover can switch the IGMP traffic from one slave to another.
    # LACP-RELATED OPTIONS - The following options are only valid in LACP (802.3ad) mode.
    bond_lacp_rate = "slow",  # Denotes the rate of LACPDU requested from the peer. The rate can be given as string or as numerical value. Valid values are slow (0) and fast (1). The default is slow.
    bond_ad_select = "stable",  # Denotes the 802.3ad aggregation selection logic. The mode can be given as string or as numerical value. Valid values are stable (0), bandwidth (1) and cound (2). The default is stable.
    bond_ad_actor_sys_prio = "65535",  # Denotes the LACP system priority. The allowed range is 1 - 65535. The default value is 65535.
    bond_ad_user_port_key = "0",  # Denotes the upper 10 bits of the port-key. he values can be from 0 - 1023. The default is 0.
    # ACTIVE/BACKUP-RELATED OPTIONS - The following options are only valid in active/passive setups.
    bond_primary = "",  # Denotes the primary member interface The specified device will always be the active slave while it is available. The primary option is only valid for active-backup, balance-tlb and balance-alb mode.
    bond_primary_reselect = "always",  # Denotes the reselection policy for the primary member interface. Valid values are always (0), better (1), and failure (2). The default is always.
    bond_fail_over_mac = "none",  # Denotes whether active-backup mode should set all member interfaces to the same MAC address at enslavement (the traditional behavior), or, when enabled, perform special handling of the bond's MAC address in accordance with the selected policy. Valid values are none (0), active (1), follow (2). The default is none.
    bond_num_grat_arp = "1",  # Denotes the number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements) to be issued after a failover event. The valid range is 0 - 255; the default is 1.
    bond_peer_notif_delay = "0",  # Denotes the interval in milliseconds, between each peer notification (gratuitous ARP and unsolicited IPv6 Neighbor Advertisement) issued after a failover event. The default is 0 which means to match the value of the link monitor interval.
    # ARP-RELATED OPTIONS -    The following options configure ARP link monitoring. The ARP monitor works by periodically checking the slave devices to determine whether they have sent or received traffic recently. Regular traffic is generated via ARP probes issued for the addresses specified by the bond-arp-ip-target option.
    bond_arp_interval = "0",  # Denotes the frequency in milliseconds to send ARP probes.
    bond_arp_ip_target = "",  # Denotes the IP addresses to use as ARP monitoring peers when bond-arp-interval is > 0.
    bond_arp_validate = "none",  # Specifies whether or not ARP probes and replies should be validated in any mode that supports arp monitoring, or whether non-ARP traffic should be filtered (disregarded) for link monitoring purposes. Valid values are none (0), active (1), backup (2), all (3), filter (4), filter_active (5), and filter_backup (6). The default is none.
    bond_arp_all_targets = "any"  # Denotes the number of bond-arp-ip-targets that have to be reachable to consider the member interface to be up. Valid options are any (0) and all (1). The default is any.
    def __init__(self,
                 name,
                 # BOND-RELATED OPTIONS
                 bond_members=[], # bond-members list of interfaces. Denotes the physical member interfaces to form this LAG. For compatiblity to ifupdown1 and ifupdown2 slaves as well as bond-slaves are an alias for this option. This option is required.
                 bond_mode="balance-rr", # bond-mode mode. Denotes the mode for this LAG. The mode can be given as string or as numerical value. Valid values are balance-rr (0), active-backup (1), balance-xor (2), broadcast (3), 802.3ad (4), balance-tlb (5), balance-alb (6). The default is balance-rr
                 bond_xmit_hash_policy="layer2", # bond-xmit-hash-policy policy. Denotes the hash policy/algorithm used to distribute packets across the physical links. This only applies for modes balance-alb, balance-tlb, balance-xor, and 802.3ad. The policy can be given as string or as numerical value. Valid values are layer2 (0), layer3+4 (1), layer2+3 (2), encap2+3 (3), and encap3+4 (4). The default is layer2.
                 bond_min_links="0", # Denotes the minimum number of available links before turning on carrier.
                 bond_miimon="0", # Denotes the MII link monitoring frequency in milliseconds. This determines how often the link state of each slave is inspected for link failures. A value of zero disables MII link monitoring. The default is 0.
                 bond_use_carrier="1", # Denotes wether miimon uses MII or ethtool ioctls vs. the netif_carrier_ok() call to determine member link status. A value of 1 enables the use of netif_carrier_ok(), a value of 0 will use the deprecated MII / ETHTOOL ioctls. The default is 1.
                 bond_updelay="0", # Denotes the delay in milliseconds before considering link up, in milliseconds. The default is 0.
                 bond_downdelay="0", # Denotes the delay in milliseconds before considering link down, in milliseconds. The default is 0.
                 bond_all_slaves_active="0", # Denotes wether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1). The default is 0.
                 bond_packets_per_slave="1", # Denotes the number of packets to transmit through a member before moving to the next one. When set to 0 then a slave is chosen at random. The valid range is 0 - 65535; the default value is 1. This option has effect only in balance-rr mode.
                 bond_lp_interval="1", # Denotes the interval in seconds between sending learning packets to each members peer switch. The valid range is 1 - 0x7fffffff; the default value is 1. This option has effect only in modes balance-tlb and balance-alb.
                 bond_resend_igmp="1", # Denotes the number of IGMP membership reports to send after a link failover happend. The valid range is 0 - 255; a value of 0 prevents the IGMP membership report from being issued in response to the failover event. The default is 1. This option is useful for bonding modes balance-rr, active-backup balance-tlb and balance-alb, in which a failover can switch the IGMP traffic from one slave to another.
                 # LACP-RELATED OPTIONS - The following options are only valid in LACP (802.3ad) mode.
                 bond_lacp_rate="slow", # Denotes the rate of LACPDU requested from the peer. The rate can be given as string or as numerical value. Valid values are slow (0) and fast (1). The default is slow.
                 bond_ad_select="stable", # Denotes the 802.3ad aggregation selection logic. The mode can be given as string or as numerical value. Valid values are stable (0), bandwidth (1) and cound (2). The default is stable.
                 bond_ad_actor_sys_prio="65535", # Denotes the LACP system priority. The allowed range is 1 - 65535. The default value is 65535.
                 bond_ad_user_port_key="0", # Denotes the upper 10 bits of the port-key. he values can be from 0 - 1023. The default is 0.
                 # ACTIVE/BACKUP-RELATED OPTIONS - The following options are only valid in active/passive setups.
                 bond_primary="", # Denotes the primary member interface The specified device will always be the active slave while it is available. The primary option is only valid for active-backup, balance-tlb and balance-alb mode.
                 bond_primary_reselect="always", # Denotes the reselection policy for the primary member interface. Valid values are always (0), better (1), and failure (2). The default is always.
                 bond_fail_over_mac="none", # Denotes whether active-backup mode should set all member interfaces to the same MAC address at enslavement (the traditional behavior), or, when enabled, perform special handling of the bond's MAC address in accordance with the selected policy. Valid values are none (0), active (1), follow (2). The default is none.
                 bond_num_grat_arp="1", # Denotes the number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements) to be issued after a failover event. The valid range is 0 - 255; the default is 1.
                 bond_peer_notif_delay="0", # Denotes the interval in milliseconds, between each peer notification (gratuitous ARP and unsolicited IPv6 Neighbor Advertisement) issued after a failover event. The default is 0 which means to match the value of the link monitor interval.
                 # ARP-RELATED OPTIONS -    The following options configure ARP link monitoring. The ARP monitor works by periodically checking the slave devices to determine whether they have sent or received traffic recently. Regular traffic is generated via ARP probes issued for the addresses specified by the bond-arp-ip-target option.
                 bond_arp_interval="0",  # Denotes the frequency in milliseconds to send ARP probes.
                 bond_arp_ip_target="", # Denotes the IP addresses to use as ARP monitoring peers when bond-arp-interval is > 0.
                 bond_arp_validate="none", # Specifies whether or not ARP probes and replies should be validated in any mode that supports arp monitoring, or whether non-ARP traffic should be filtered (disregarded) for link monitoring purposes. Valid values are none (0), active (1), backup (2), all (3), filter (4), filter_active (5), and filter_backup (6). The default is none.
                 bond_arp_all_targets="any" # Denotes the number of bond-arp-ip-targets that have to be reachable to consider the member interface to be up. Valid options are any (0) and all (1). The default is any.
                 ):
        super().__init__(name)
        self.type = "bond"
        self.name = name
        path = '/sys/class/net/' + name

        if os.path.isdir(path):

            # проверяемя является интерфейс членом моста
            if os.path.isdir(path + '/brport'):
                # bridge member
                self.bridge_member = True
                self.master = os.path.realpath(path + '/master').split('/')[-1]

            #bond_members
            try:
                f = open(path + '/bonding/slaves', 'r')
                bonding = f.read().rstrip()
            finally:
                f.close()
            members = []
            for member in bonding.split():
                members.append(member)
            self.bond_members = members

            #self.bond_mode = bond_mode
            f = open(path + '/bonding/mode', 'r')
            try:
                bond_mode = f.read().rstrip()
            finally:
                f.close()
            self.bond_mode = bond_mode.split(' ')[0]

            #self.bond_xmit_hash_policy = bond_xmit_hash_policy
            f = open(path + '/bonding/xmit_hash_policy', 'r')
            try:
                xmit_hash_policy = f.read().rstrip()
            finally:
                f.close()
            self.bond_xmit_hash_policy = xmit_hash_policy.split(' ')[0]

            #self.bond_min_links = bond_min_links
            f = open(path + '/bonding/min_links', 'r')
            try:
                min_links = f.read().rstrip()
            finally:
                f.close()
            self.bond_min_links = min_links.split(' ')[0]

            #self.bond_miimon = bond_miimon
            f = open(path + '/bonding/miimon', 'r')
            try:
                miimon = f.read().rstrip()
            finally:
                f.close()
            self.bond_miimon = miimon.split(' ')[0]

            #self.bond_use_carrier = bond_use_carrier
            f = open(path + '/bonding/use_carrier', 'r')
            try:
                use_carrier = f.read().rstrip()
            finally:
                f.close()
            self.bond_use_carrier = use_carrier.split(' ')[0]

            #self.bond_updelay = bond_updelay
            f = open(path + '/bonding/updelay', 'r')
            try:
                updelay = f.read().rstrip()
            finally:
                f.close()
            self.bond_updelay = updelay.split(' ')[0]

            #self.bond_downdelay = bond_downdelay
            f = open(path + '/bonding/downdelay', 'r')
            try:
                downdelay = f.read().rstrip()
            finally:
                f.close()
            self.bond_downdelay = downdelay.split(' ')[0]

            #self.bond_all_slaves_active = bond_all_slaves_active
            f = open(path + '/bonding/all_slaves_active', 'r')
            try:
                all_slaves_active = f.read().rstrip()
            finally:
                f.close()
            self.bond_all_slaves_active = all_slaves_active.split(' ')[0]

            #self.bond_packets_per_slave = bond_packets_per_slave
            f = open(path + '/bonding/packets_per_slave', 'r')
            try:
                packets_per_slave = f.read().rstrip()
            finally:
                f.close()
            self.bond_packets_per_slave = packets_per_slave.split(' ')[0]

            #self.bond_lp_interval = bond_lp_interval
            f = open(path + '/bonding/lp_interval', 'r')
            try:
                lp_interval = f.read().rstrip()
            finally:
                f.close()
            self.bond_lp_interval = lp_interval.split(' ')[0]

            #self.bond_resend_igmp = bond_resend_igmp
            f = open(path + '/bonding/resend_igmp', 'r')
            try:
                resend_igmp = f.read().rstrip()
            finally:
                f.close()
            self.bond_resend_igmp = resend_igmp.split(' ')[0]

            # LACP-RELATED OPTIONS - The following options are only valid in LACP (802.3ad) mode.
            if self.bond_mode in {"802.3ad", "4"}:
                #self.bond_lacp_rate = bond_lacp_rate
                f = open(path + '/bonding/lacp_rate', 'r')
                try:
                    lacp_rate = f.read().rstrip()
                finally:
                    f.close()
                self.bond_lacp_rate = lacp_rate.split(' ')[0]

                #self.bond_ad_select = bond_ad_select
                f = open(path + '/bonding/ad_select', 'r')
                try:
                    ad_select = f.read().rstrip()
                finally:
                    f.close()
                self.bond_ad_select = ad_select.split(' ')[0]

                #self.bond_ad_actor_sys_prio = bond_ad_actor_sys_prio
                f = open(path + '/bonding/ad_actor_sys_prio', 'r')
                try:
                    ad_actor_sys_prio = f.read().rstrip()
                finally:
                    f.close()
                self.bond_ad_actor_sys_prio = ad_actor_sys_prio.split(' ')[0]

                #self.bond_ad_user_port_key = bond_ad_user_port_key
                f = open(path + '/bonding/ad_user_port_key', 'r')
                try:
                    ad_user_port_key = f.read().rstrip()
                finally:
                    f.close()
                self.bond_ad_user_port_key = ad_user_port_key.split(' ')[0]

            # ACTIVE/BACKUP-RELATED OPTIONS - The following options are only valid in active/passive setups.
            if self.bond_mode in {"active-backup", "1", "balance-tlb", "5", "balance-alb", "6"}:
                #self.bond_primary = bond_primary
                f = open(path + '/bonding/primary', 'r')
                try:
                    primary = f.read().rstrip()
                finally:
                    f.close()
                self.bond_primary = primary.split(' ')[0]

                #self.bond_primary_reselect = bond_primary_reselect
                f = open(path + '/bonding/primary_reselect', 'r')
                try:
                    primary_reselect = f.read().rstrip()
                finally:
                    f.close()
                self.bond_primary_reselect = primary_reselect.split(' ')[0]

                #self.bond_fail_over_mac = bond_fail_over_mac
                f = open(path + '/bonding/fail_over_mac', 'r')
                try:
                    fail_over_mac = f.read().rstrip()
                finally:
                    f.close()
                self.bond_fail_over_mac = fail_over_mac.split(' ')[0]

                #self.bond_num_grat_arp = bond_num_grat_arp
                f = open(path + '/bonding/num_grat_arp', 'r')
                try:
                    num_grat_arp = f.read().rstrip()
                finally:
                    f.close()
                self.bond_num_grat_arp = num_grat_arp.split(' ')[0]

                #self.bond_peer_notif_delay = bond_peer_notif_delay
                f = open(path + '/bonding/peer_notif_delay', 'r')
                try:
                    peer_notif_delay = f.read().rstrip()
                finally:
                    f.close()
                self.bond_peer_notif_delay = peer_notif_delay.split(' ')[0]

            # ARP-RELATED OPTIONS -    The following options configure ARP link monitoring. The ARP monitor works by periodically checking the slave devices to determine whether they have sent or received traffic recently. Regular traffic is generated via ARP probes issued for the addresses specified by the bond-arp-ip-target option.
            #self.bond_arp_interval = bond_arp_interval
            f = open(path + '/bonding/arp_interval', 'r')
            try:
                arp_interval = f.read().rstrip()
            finally:
                f.close()
            self.bond_arp_interval = arp_interval.split(' ')[0]

            #self.bond_arp_ip_target = bond_arp_ip_target
            f = open(path + '/bonding/arp_ip_target', 'r')
            try:
                arp_ip_target = f.read().rstrip()
            finally:
                f.close()
            self.bond_arp_ip_target = arp_ip_target.split(' ')[0]

            #self.bond_arp_validate = bond_arp_validate
            f = open(path + '/bonding/arp_validate', 'r')
            try:
                arp_validate = f.read().rstrip()
            finally:
                f.close()
            self.bond_arp_validate = arp_validate.split(' ')[0]

            #self.bond_arp_all_targets = bond_arp_all_targets
            f = open(path + '/bonding/arp_all_targets', 'r')
            try:
                arp_all_targets = f.read().rstrip()
            finally:
                f.close()
            self.bond_arp_all_targets = arp_all_targets.split(' ')[0]

        else:
            self.bond_members = bond_members
            self.bond_mode = bond_mode
            self.bond_xmit_hash_policy = bond_xmit_hash_policy
            self.bond_min_links = bond_min_links
            self.bond_miimon = bond_miimon
            self.bond_use_carrier = bond_use_carrier
            self.bond_updelay = bond_updelay
            self.bond_downdelay = bond_downdelay
            self.bond_all_slaves_active = bond_all_slaves_active
            self.bond_packets_per_slave = bond_packets_per_slave
            self.bond_lp_interval = bond_lp_interval
            self.bond_resend_igmp = bond_resend_igmp
            #LACP-RELATED OPTIONS - The following options are only valid in LACP (802.3ad) mode.
            self.bond_lacp_rate = bond_lacp_rate
            self.bond_ad_select = bond_ad_select
            self.bond_ad_actor_sys_prio = bond_ad_actor_sys_prio
            self.bond_ad_user_port_key = bond_ad_user_port_key
            #ACTIVE/BACKUP-RELATED OPTIONS - The following options are only valid in active/passive setups.
            self.bond_primary = bond_primary
            self.bond_primary_reselect = bond_primary_reselect
            self.bond_fail_over_mac = bond_fail_over_mac
            self.bond_num_grat_arp = bond_num_grat_arp
            self.bond_peer_notif_delay = bond_peer_notif_delay
            #ARP-RELATED OPTIONS -    The following options configure ARP link monitoring. The ARP monitor works by periodically checking the slave devices to determine whether they have sent or received traffic recently. Regular traffic is generated via ARP probes issued for the addresses specified by the bond-arp-ip-target option.
            self.bond_arp_interval = bond_arp_interval
            self.bond_arp_ip_target = bond_arp_ip_target
            self.bond_arp_validate = bond_arp_validate
            self.bond_arp_all_targets = bond_arp_all_targets

    def get_init_script(self, all=False):
        script = "\n"
        if self.up:
            script = script + str(f"auto {self.name}\n")
        if self.inet == "dhcp":
            script = script + str(f"iface {self.name} inet dhcp\n")
        elif self.inet == "static":
            script = script + str(f"iface {self.name} inet static\n")
            script = script + str(f"\taddress {self.ip}/{self.netmask}\n")
            if self.gateway:
                script = script + str(f"\tgateway {self.gateway}\n")
            if self.dns1 != "":
                self.fqdn = os.uname().nodename
                self.hostname = self.fqdn.split(".")[0]
                self.domainname = self.fqdn[self.fqdn.index(self.hostname) + len(self.hostname) + 1:]
                if self.domainname != "":
                    script = script + str(f"\tdns-search {self.domainname}\n")
                dns_nameservers = str(self.dns1)
                if self.dns2 != "":
                    dns_nameservers = dns_nameservers + f" {str(self.dns2)}"
                script = script + str(f"\tdns-nameservers {dns_nameservers}\n")
        else:
            script = script + str(f"iface {self.name} inet manual\n")

        # mtu.
        if str(self.mtu) != "1500" or all:
            script = script + str(f"\tmtu {self.mtu}\n")

        # bond-members list of interfaces.
        bond_members = ""
        if self.bond_members != "":
            for bond_member in self.bond_members:
                bond_members = str(bond_members) + str(bond_member) + " "
        else:
            bond_members = "none"
        script = script + str(f"\tbond-slaves {bond_members}\n")

        #bond-mode mode. Denotes the mode for this LAG. The mode can be given as string or as numerical value. Valid values are balance-rr (0), active-backup (1), balance-xor (2), broadcast (3), 802.3ad (4), balance-tlb (5), balance-alb (6). The default is balance-rr
        if self.bond_mode not in ["balance-rr", "0"] or all:
            script = script + str(f"\tbond-mode {self.bond_mode}\n")

        # bond-xmit-hash-policy policy. Denotes the hash policy/algorithm used to distribute packets across the physical links. This only applies for modes balance-alb, balance-tlb, balance-xor, and 802.3ad. The policy can be given as string or as numerical value. Valid values are layer2 (0), layer3+4 (1), layer2+3 (2), encap2+3 (3), and encap3+4 (4). The default is layer2.
        if self.bond_mode in ["balance-alb", "6", "balance-tlb", "5", "balance-xor", "2", "802.3ad", "4"]:
            if self.bond_xmit_hash_policy not in ["layer2", "0"] or all:
                script = script + str(f"\tbond-xmit-hash-policy {self.bond_xmit_hash_policy}\n")

        # Denotes the minimum number of available links before turning on carrier.
        if str(self.bond_min_links) != "0" or all:
            script = script + str(f"\tbond-min-links {self.bond_min_links}\n")

        # Denotes the MII link monitoring frequency in milliseconds. This determines how often the link state of each slave is inspected for link failures. A value of zero disables MII link monitoring. The default is 0.
        if self.bond_miimon  != "0" or all:
            script = script + str(f"\tbond-miimon {self.bond_miimon}\n")

        # Denotes wether miimon uses MII or ethtool ioctls vs. the netif_carrier_ok() call to determine member link status. A value of 1 enables the use of netif_carrier_ok(), a value of 0 will use the deprecated MII / ETHTOOL ioctls. The default is 1.
        if self.bond_use_carrier  != "1" or all:
            script = script + str(f"\tbond-use-carrier {self.bond_use_carrier}\n")

        # Denotes the delay in milliseconds before considering link up, in milliseconds. The default is 0.
        if self.bond_updelay  != "0" or all:
            script = script + str(f"\tbond-updelay {self.bond_updelay}\n")

        # Denotes the delay in milliseconds before considering link down, in milliseconds. The default is 0.
        if self.bond_downdelay  != "0" or all:
            script = script + str(f"\tbond-downdelay {self.bond_downdelay}\n")

        # Denotes wether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1). The default is 0.
        if self.bond_all_slaves_active != "0" or all:
            script = script + str(f"\tbond-all-slaves-active {self.bond_all_slaves_active}\n")

        # Denotes the number of packets to transmit through a member before moving to the next one. When set to 0 then a slave is chosen at random. The valid range is 0 - 65535; the default value is 1. This option has effect only in balance-rr mode.
        if self.bond_packets_per_slave != "1" or all:
            script = script + str(f"\tbond-packets-per-slave {self.bond_packets_per_slave}\n")

        # Denotes the interval in seconds between sending learning packets to each members peer switch. The valid range is 1 - 0x7fffffff; the default value is 1. This option has effect only in modes balance-tlb and balance-alb.
        if self.bond_lp_interval != "1" or all:
            script = script + str(f"\tbond-lp-interval {self.bond_lp_interval}\n")

        # Denotes the number of IGMP membership reports to send after a link failover happend. The valid range is 0 - 255; a value of 0 prevents the IGMP membership report from being issued in response to the failover event. The default is 1. This option is useful for bonding modes balance-rr, active-backup balance-tlb and balance-alb, in which a failover can switch the IGMP traffic from one slave to another.
        if self.bond_resend_igmp != "1" or all:
            script = script + str(f"\tbond-resend-igmp {self.bond_resend_igmp}\n")

        #LACP-RELATED OPTIONS - The following options are only valid in LACP (802.3ad) mode.
        if self.bond_mode in ["802.3ad", "4"]:
            # Denotes the rate of LACPDU requested from the peer. The rate can be given as string or as numerical value. Valid values are slow (0) and fast (1). The default is slow.
            if self.bond_lacp_rate not in ["slow", "0"] or all:
                script = script + str(f"\tbond-lacp-rate {self.bond_lacp_rate}\n")
            # Denotes the 802.3ad aggregation selection logic. The mode can be given as string or as numerical value. Valid values are stable (0), bandwidth (1) and cound (2). The default is stable.
            if self.bond_ad_select not in ["stable", "0"] or all:
                script = script + str(f"\tbond-ad-select {self.bond_ad_select}\n")
            # Denotes the LACP system priority. The allowed range is 1 - 65535. The default value is 65535.
            if self.bond_ad_actor_sys_prio not in ["65535", ""]:
                script = script + str(f"\tbond-ad-actor-sys-prio {self.bond_ad_actor_sys_prio}\n")
            elif all:
                script = script + str(f"\tbond-ad-actor-sys-prio 65535\n")
            # Denotes the upper 10 bits of the port-key. he values can be from 0 - 1023. The default is 0.
            if self.bond_ad_user_port_key not in ["0", ""]:
                script = script + str(f"\tbond-ad-user-port-key {self.bond_ad_user_port_key}\n")
            elif all:
                script = script + str(f"\tbond-ad-user-port-key 0\n")

        # ACTIVE/BACKUP-RELATED OPTIONS - The following options are only valid in active/passive setups.
        if self.bond_mode in ["active-backup", "1", "balance-tlb", "5", "balance-alb", "6"]:
            # Denotes the primary member interface The specified device will always be the active slave while it is available. The primary option is only valid for active-backup, balance-tlb and balance-alb mode.
            if self.bond_primary != "":
                script = script + str(f"\tbond-primary {self.bond_primary}\n")
            # Denotes the reselection policy for the primary member interface. Valid values are always (0), better (1), and failure (2). The default is always.
            if self.bond_primary_reselect != "always" or all:
                script = script + str(f"\tbond-primary-reselect {self.bond_primary_reselect}\n")
            # Denotes whether active-backup mode should set all member interfaces to the same MAC address at enslavement (the traditional behavior), or, when enabled, perform special handling of the bond's MAC address in accordance with the selected policy. Valid values are none (0), active (1), follow (2). The default is none.
            if self.bond_fail_over_mac  != "none" or all:
                script = script + str(f"\tbond-fail-over-mac {self.bond_fail_over_mac}\n")
            # Denotes the number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements) to be issued after a failover event. The valid range is 0 - 255; the default is 1.
            if self.bond_num_grat_arp != "1" or all:
                script = script + str(f"\tbond-num-grat-arp {self.bond_num_grat_arp}\n")
            # Denotes the interval in milliseconds, between each peer notification (gratuitous ARP and unsolicited IPv6 Neighbor Advertisement) issued after a failover event. The default is 0 which means to match the value of the link monitor interval.
            if self.bond_peer_notif_delay != "0" or all:
                script = script + str(f"\tbond-peer-notif-delay {self.bond_peer_notif_delay}\n")

        # ARP-RELATED OPTIONS -    The following options configure ARP link monitoring. The ARP monitor works by periodically checking the slave devices to determine whether they have sent or received traffic recently. Regular traffic is generated via ARP probes issued for the addresses specified by the bond-arp-ip-target option.
            # Denotes the frequency in milliseconds to send ARP probes.
            if self.bond_arp_interval != "0" or all:
                script = script + str(f"\tbond-arp-interval {self.bond_arp_interval}\n")
            # Denotes the IP addresses to use as ARP monitoring peers when bond-arp-interval is > 0.
            if self.bond_arp_ip_target != "":
                script = script + str(f"\tbond-arp-ip-target {self.bond_arp_ip_target}\n")
            # Specifies whether or not ARP probes and replies should be validated in any mode that supports arp monitoring, or whether non-ARP traffic should be filtered (disregarded) for link monitoring purposes. Valid values are none (0), active (1), backup (2), all (3), filter (4), filter_active (5), and filter_backup (6). The default is none.
            if self.bond_arp_validate not in ["none", ""]:
                script = script + str(f"\tbond-arp-validate {self.bond_arp_validate}\n")
            elif all:
                script = script + str(f"\tbond-arp-validate none\n")
            # Denotes the number of bond-arp-ip-targets that have to be reachable to consider the member interface to be up. Valid options are any (0) and all (1). The default is any.
            if self.bond_arp_all_targets not in ["any", ""]:
                script = script + str(f"\tbond-arp-all-targets {self.bond_arp_all_targets}\n")
            elif all:
                script = script + str(f"\tbond-arp-all-targets any\n")
        return script

class Bridge(Interface):
    def __init__(self,
                 name,
                 # BRIDGE-RELATED OPTIONS
                 bridge_ports = [],  # list of interfaces. A space separated list of interfaces which should be configured as member interfaces of this bridge. This option must be set for the bridge to be configured.
                 bridge_hw = None,  # MAC address. Denotes the MAC address the bridge should use.
                 bridge_ageing = "300",  # seconds. Denotes the time in seconds after which a MAC address will be removed from the Forwarding DataBase (FDB) after not having seen a frame with this source address.
                 bridge_vlan_aware = "no",  # Denotes wether or not the bridge should be aware of 802.1q VLANs. bool can be given as yes/no or 0/1. The defaul is no. See related options for configuring vlan-aware bridges, below.
                 # SPANNING TREE RELATED BRIDGE OPTIONS¶
                 bridge_stp = "off",  # Activates or deactivates IEEE 802.1d Spanning Tree Protocol (STP) support of the bridge. Valid values are on/off.
                 bridge_bridgeprio = "32768",  # Sets the bridge's priority to priority. The priority value is a number between 0 and 65535. Lower priority values are better. The bridge with the lowest priority will be elected root bridge.
                 bridge_fd = "15",  # Denotes the bridge forward delay in seconds. Valid values are between 2 and 30.
                 bridge_hello = "2",  # Denotes the bridge hello time in seconds. Valid values are between 1 and 10.
                 bridge_maxage = "20",  # Denotes the seconds until another bridge is considerd dead after reception of its last STP hello message. Valid values are between 6 and 40.
                 # OPTIONS FOR VLAN-AWARE-BRIDGES
                 # The following options only have an effect on vlan-aware bridges and their ports.
                 # All settings can be applied on the bridge interface itself and all member port iface stanzas. If applied on the bridge interface they take effect for the bridge interface itself and might be inherited to bridge-ports depending on the compatibility settings configured in ifupdown-ng.conf(5).
                 # Configuring VLAN options on the bridge interface might be required for setting up a VLAN interface to one of the VLANs carried within the bridge. See the EXAMPLES section for an example for this scenario.
                 bridge_access = None,  # vlan ID. Configure the given vlan ID for untagged ingress and egress on this interface. The common description for this kind of configuration is called "access port".
                 bridge_pvid = None,  # vlan ID. Denotes the vlan ID to considered a PVID at ingress. Any untagged frames received on this interface will be assigned to this vlan ID. The default PVID is 1. If compatibility to ifupdown2 bridge port inheritance is active a bridge-pvid set on the bridge will be inherited to any interface configured in bridge-ports without a bridge-pvid set.
                 bridge_vids = [],  # list of vlan IDs. Denotes the space separated list of VLANs to be allowed tagged ingress/egress on this interface. If compatibility to ifupdown2 bridge port inheritance is active a bridge-vids set on the bridge will be inherited to any interface configured in bridge-ports without bridge-vids set.
                 bridge_allow_untagged = "yes"  # bool. Denotes wether or not the bridge should allow untagged frames on ingress as well as egress. If set to no untagged frames will be droppped on ingress and none will be sent. bool can be given as yes/no or 0/1. The defaul is yes.
                 ):
        super().__init__(name)
        self.type = "bridge"
        self.name = name
        path = '/sys/class/net/' + name

        if os.path.isdir(path):
            #bridge_members
            self.bridge_ports = os.listdir(path + '/brif/')

            #self.bridge_hw = bridge_hw
            f = open(path + '/address', 'r')
            try:
                address = f.read().rstrip()
            finally:
                f.close()
            self.bridge_hw = address.split(' ')[0]


            #self.bridge_ageing = bridge_ageing
            f = open(path + '/bridge/ageing_time', 'r')
            try:
                ageing_time = f.read().rstrip()
            finally:
                f.close()
            self.bridge_ageing = ageing_time.split(' ')[0][:-2]

            #self.bridge_vlan_aware = bridge_vlan_aware
            # пока не реализовано
            # ********

            # SPANNING TREE RELATED BRIDGE OPTIONS¶
            #self.bridge_stp = bridge_stp
            f = open(path + '/bridge/stp_state', 'r')
            try:
                bridge_stp = f.read().rstrip()
            finally:
                f.close()
            if bridge_stp == "1":
                self.bridge_stp = "on"
            else:
                self.bridge_stp = "off"

            #self.bridge_bridgeprio = bridge_bridgeprio
            f = open(path + '/bridge/priority', 'r')
            try:
                bridge_bridgeprio = f.read().rstrip()
            finally:
                f.close()
            self.bridge_bridgeprio = bridge_bridgeprio.split(' ')[0]

            #self.bridge_fd = bridge_fd
            f = open(path + '/bridge/forward_delay', 'r')
            try:
                forward_delay = f.read().rstrip()
            finally:
                f.close()
            self.bridge_fd = forward_delay.split(' ')[0][:-2]

            #self.bridge_hello = bridge_hello
            f = open(path + '/bridge/hello_time', 'r')
            try:
                hello_time = f.read().rstrip()
            finally:
                f.close()
            self.bridge_hello = hello_time.split(' ')[0][:-2]

            #self.bridge_maxage = bridge_maxage
            f = open(path + '/bridge/max_age', 'r')
            try:
                max_age = f.read().rstrip()
            finally:
                f.close()
            self.bridge_maxage = max_age.split(' ')[0][:-2]


            # OPTIONS FOR VLAN-AWARE-BRIDGES
            # The following options only have an effect on vlan-aware bridges and their ports.
            # All settings can be applied on the bridge interface itself and all member port iface stanzas. If applied on the bridge interface they take effect for the bridge interface itself and might be inherited to bridge-ports depending on the compatibility settings configured in ifupdown-ng.conf(5).
            # Configuring VLAN options on the bridge interface might be required for setting up a VLAN interface to one of the VLANs carried within the bridge. See the EXAMPLES section for an example for this scenario.
            #self.bridge_access = bridge_access
            #self.bridge_pvid = bridge_pvid
            #self.bridge_vids = bridge_vids
            #self.bridge_allow_untagged = bridge_allow_untagged



        else:
            #BRIDGE-RELATED OPTIONS
            self.bridge_ports = bridge_ports
            self.bridge_hw = bridge_hw
            self.bridge_ageing = bridge_ageing
            self.bridge_vlan_aware = bridge_vlan_aware
            #SPANNING TREE RELATED BRIDGE OPTIONS¶
            self.bridge_stp = bridge_stp
            self.bridge_bridgeprio = bridge_bridgeprio
            self.bridge_fd = bridge_fd
            self.bridge_hello = bridge_hello
            self.bridge_maxage = bridge_maxage
            #OPTIONS FOR VLAN-AWARE-BRIDGES
            #The following options only have an effect on vlan-aware bridges and their ports.
            #All settings can be applied on the bridge interface itself and all member port iface stanzas. If applied on the bridge interface they take effect for the bridge interface itself and might be inherited to bridge-ports depending on the compatibility settings configured in ifupdown-ng.conf(5).
            #Configuring VLAN options on the bridge interface might be required for setting up a VLAN interface to one of the VLANs carried within the bridge. See the EXAMPLES section for an example for this scenario.
            self.bridge_access = bridge_access
            self.bridge_pvid = bridge_pvid
            self.bridge_vids = bridge_vids
            self.bridge_allow_untagged = bridge_allow_untagged

    def get_init_script(self, all=False):
        script = "\n"
        if self.up:
            script = script + str(f"auto {self.name}\n")
        if self.inet == "dhcp":
            script = script + str(f"iface {self.name} inet dhcp\n")
        elif self.inet == "static":
            script = script + str(f"iface {self.name} inet static\n")
            script = script + str(f"\taddress {self.ip}/{self.netmask}\n")
            if self.gateway:
                script = script + str(f"\tgateway {self.gateway}\n")
            if self.dns1 != "":
                self.fqdn = os.uname().nodename
                self.hostname = self.fqdn.split(".")[0]
                self.domainname = self.fqdn[self.fqdn.index(self.hostname) + len(self.hostname) + 1:]
                if self.domainname != "":
                    script = script + str(f"\tdns-search {self.domainname}\n")
                dns_nameservers = str(self.dns1)
                if self.dns2 != "":
                    dns_nameservers = dns_nameservers + f" {str(self.dns2)}"
                script = script + str(f"\tdns-nameservers {dns_nameservers}\n")
        else:
            script = script + str(f"iface {self.name} inet manual\n")

        # list of interfaces. A space separated list of interfaces which should be configured as member interfaces of this bridge. This option must be set for the bridge to be configured.
        ports = ""
        if self.bridge_ports != "":
            for port in self.bridge_ports:
                ports = ports + port + " "
        else:
            ports = "none"
        script = script + str(f"\tbridge-ports {ports}\n")

        # MAC address. Denotes the MAC address the bridge should use.
        #if self.bridge_hw != None:
        #    script = script + str(f"\tbridge-hw {self.bridge_hw}\n")

        # seconds. Denotes the time in seconds after which a MAC address will be removed from the Forwarding DataBase (FDB) after not having seen a frame with this source address.
        if self.bridge_ageing != "300" or all:
            script = script + str(f"\tbridge-ageing {self.bridge_ageing}\n")

        # Denotes wether or not the bridge should be aware of 802.1q VLANs. bool can be given as yes/no or 0/1. The defaul is no. See related options for configuring vlan-aware bridges, below.
        #if self.bridge_vlan_aware not in ["no", 0]:
        #    script = script + str(f"\tbridge-vlan-aware {self.bridge_vlan_aware}\n")

        # SPANNING TREE RELATED BRIDGE OPTIONS¶
        # Activates or deactivates IEEE 802.1d Spanning Tree Protocol (STP) support of the bridge. Valid values are on/off.
        if self.bridge_stp != "off" or all:
            script = script + str(f"\tbridge-stp {self.bridge_stp}\n")

        # Sets the bridge's priority to priority. The priority value is a number between 0 and 65535. Lower priority values are better. The bridge with the lowest priority will be elected root bridge.
        if self.bridge_bridgeprio != "32768" or all:
            script = script + str(f"\tbridge-bridgeprio {self.bridge_bridgeprio}\n")

        # Denotes the bridge forward delay in seconds. Valid values are between 2 and 30.
        if self.bridge_fd != "15" or all:
            script = script + str(f"\tbridge-fd {self.bridge_fd}\n")

        # Denotes the bridge hello time in seconds. Valid values are between 1 and 10.
        if self.bridge_hello != "2" or all:
            script = script + str(f"\tbridge-hello {self.bridge_hello}\n")

        # Denotes the seconds until another bridge is considerd dead after reception of its last STP hello message. Valid values are between 6 and 40.
        if self.bridge_maxage != "20" or all:
            script = script + str(f"\tbridge-maxage {self.bridge_maxage}\n")

        # OPTIONS FOR VLAN-AWARE-BRIDGES
        # The following options only have an effect on vlan-aware bridges and their ports.
        # All settings can be applied on the bridge interface itself and all member port iface stanzas. If applied on the bridge interface they take effect for the bridge interface itself and might be inherited to bridge-ports depending on the compatibility settings configured in ifupdown-ng.conf(5).
        # Configuring VLAN options on the bridge interface might be required for setting up a VLAN interface to one of the VLANs carried within the bridge. See the EXAMPLES section for an example for this scenario.

        # vlan ID. Configure the given vlan ID for untagged ingress and egress on this interface. The common description for this kind of configuration is called "access port".
        #if self.bridge_access != None:
        #    script = script + str(f"\tbridge-access {self.bridge_access}\n")

        # vlan ID. Denotes the vlan ID to considered a PVID at ingress. Any untagged frames received on this interface will be assigned to this vlan ID. The default PVID is 1. If compatibility to ifupdown2 bridge port inheritance is active a bridge-pvid set on the bridge will be inherited to any interface configured in bridge-ports without a bridge-pvid set.
        #if self.bridge_pvid != None:
        #    script = script + str(f"\tbridge-pvid {self.bridge_pvid}\n")

        # list of vlan IDs. Denotes the space separated list of VLANs to be allowed tagged ingress/egress on this interface. If compatibility to ifupdown2 bridge port inheritance is active a bridge-vids set on the bridge will be inherited to any interface configured in bridge-ports without bridge-vids set.
        #if self.bridge_vids != []:
        #    script = script + str(f"\tbridge-vids {self.bridge_vids}\n")

        # Denotes wether or not the bridge should allow untagged frames on ingress as well as egress. If set to no untagged frames will be droppped on ingress and none will be sent. bool can be given as yes/no or 0/1. The defaul is yes.
        #if self.bridge_allow_untagged not in ["yes", 1]:
        #    script = script + str(f"\tbridge-allow-untagged {self.bridge_allow_untagged}\n")

        return script

class Vlan(Interface):
    def __init__(self, name, vlan_raw_device=""):
        super().__init__(name)
        self.type = "vlan"
        self.name = name
        path = '/sys/class/net/' + name
        if os.path.isdir(path):
            # проверяемя является интерфейс членом моста
            if os.path.isdir(path + '/brport'):
                # bridge member
                self.bridge_member = True
                self.master = os.path.realpath(path + '/master').split('/')[-1]

            if name[:4] == "vlan":
                self.vlan_id = name[4:]
                listdir = os.listdir(path)
                for item in listdir:
                    if len(item) > 8 and item[:6] == "lower_":
                        self.vlan_raw_device = item[6:]
            else:
                self.vlan_raw_device = name.split(".")[0]
                self.vlan_id = name.split(".")[1]

            #ip-proxy-arp 0 | 1 Turn proxy - arp off or on for this specific interface.This  also  works  on  plain ethernet like devices.
            #ip-rp-filter 0 | 1 | 2 Set the return path filter for this specific interface.This also works on plain ethernet like devices.
            #hw-mac-address mac - address This sets the mac address of the interface before bringing it  up.
        else:
            if name[:4] == "vlan":
                self.vlan_id = name[4:]
                self.vlan_raw_device = vlan_raw_device
            else:
                self.vlan_raw_device = name.split(".")[0]
                self.vlan_id = name.split(".")[1]
            self.ip = ""
            self.netmask = ""
            self.gateway = ""
            self.inet = "manual"

    def get_init_script(self, all=False):
        script = "\n"
        script = script + str(f"auto {self.name}\n")
        if self.inet == "dhcp":
            script = script + str(f"iface {self.name} inet dhcp\n")
        elif self.inet == "static":
            script = script + str(f"iface {self.name} inet static\n")
            script = script + str(f"\taddress {self.ip}/{self.netmask}\n")
            if self.gateway:
                script = script + str(f"\tgateway {self.gateway}\n")
        else:
            script = script + str(f"iface {self.name} inet manual\n")

        if self.vlan_raw_device != "":
            script = script + str(f"\tvlan-raw-device {self.vlan_raw_device}\n")

        return script

class Network():
    def __init__(self):
        self.nics = []
        self.bonds = []
        self.bridges = []
        self.vlans = []
        self.main_ip = astra.get_ip()
        interfaces = socket.if_nameindex()
        for interface in interfaces:
            name = str(interface[1])
            type = self.get_iface_type(name)
            if type == "ethernet":
                self.nics.append(Ethernet_interface(name))
            elif type == "bond":
                self.bonds.append(Bond(name))
            elif type == "vlan":
                self.vlans.append(Vlan(name))
            elif type == "bridge":
                self.bridges.append(Bridge(name))
        for vlan in self.vlans:
            self.get_interface_object_by_name(vlan.vlan_raw_device).vlans_count =+ 1



    def get_iface_type(self, ifname):
        path = '/sys/class/net/' + ifname
        type = ''
        with open(path + '/type') as file_object:
            type_id = file_object.read()
            if type_id == '1\n':
                type = 'ethernet'
                if os.path.isdir(path + '/wireless') or os.path.islink(path + '/phy80211'):
                    type = 'wlan'
                if os.path.isdir(path + '/bridge'):
                    type = 'bridge'
                if os.path.isfile('/proc/net/vlan/' + ifname):
                    type = 'vlan'
                if os.path.isdir(path + '/bonding'):
                    type = 'bond'
                if os.path.isfile(path + '/tun_flags'):
                    type = 'tap'
                if os.path.isdir('/sys/devices/virtual/net/' + ifname):
                    if ifname[:4] == 'dummy':
                        type = 'dummy'
            if type_id == '24\n':
                # firewire ;; # IEEE 1394 IPv4 - RFC 2734
                type = 'ethernet'
            if type_id == '32\n':
                # InfiniBand
                if os.path.isdir(path + '/bonding'):
                    type = 'bond'
                if os.path.isdir(path + '/create_child'):
                    type = 'ib'
                else:
                    type = 'ibchild'
            if type_id == '512\n':
                # firewire ;; # IEEE 1394 IPv4 - RFC 2734
                type = 'ppp'
            if type_id == '768\n':
                # IPIP tunnel
                type = 'ipip'
            if type_id == '769\n':
                # IP6IP6 tunnel
                type = 'ip6tnl'
            if type_id == '772\n':
                type = 'lo'
            if type_id == '776\n':
                # sit0 device - IPv6-in-IPv4
                type = 'sit'
            if type_id == '512\n':
                # GRE over IP
                type = 'gre'
            if type_id == '783\n':
                # Linux-IrDA
                type = 'irda'
            if type_id == '801\n':
                type = 'wlan_aux'
            if type_id == '65534\n':
                type = 'tun'
            if ifname[:3] in {'ippp', 'isdn'}:
                type = 'isdn'
            if ifname[:7] in {'mip6mnha'}:
                type = 'mip6mnha'
        return type

    def get_interface_object_by_name(self, name):
        for nic in self.nics:
            if nic.name == name:
                return nic
        for bond in self.bonds:
            if bond.name == name:
                return bond
        for vlan in self.vlans:
            if vlan.name == name:
                return vlan
        for bridge in self.bridges:
            if bridge.name == name:
                return bridge
        return None

